#include "MusicCodec.h"

inline int Scale(int sample) {
	sample += (1L << (MAD_F_FRACBITS - 16));

	if (sample >= MAD_F_ONE)
		sample = MAD_F_ONE - 1;
	else if (sample < -MAD_F_ONE)
		sample = -MAD_F_ONE;

	sample = (sample >> (MAD_F_FRACBITS + 1 - 16));
	return sample;
}

void MusicCodec::ChangeFile(std::string FileName) {
    /*if (file)
        delete file;
    file = new std::ifstream(FileName.c_str(), std::ifstream::binary);*/
}

MusicCodec::MusicCodec(std::string FileName) {
	ScaleTo = OtterSDK->GetBitScaling();

	FileBufferSize = OtterSDK->GetFileBufferSize();
	PCMBufferSize = OtterSDK->GetPCMBufferSize();
	BufferPosition = 0;

	file = new std::ifstream(FileName.c_str(), std::ifstream::binary);
	mad_decoder_init(&decoder, this,
			[] (void* data, mad_stream* stream) -> mad_flow { return ((MusicCodec*)data)->InputRequest(stream); },
				NULL, NULL,
			[] (void* data, const mad_header* header, mad_pcm* pcm) -> mad_flow { return ((MusicCodec*)data)->OutputPreCallback(header, pcm); },
				NULL, NULL);

	str = new char[FileBufferSize + 1];
};

int MusicCodec::BeginDecoding() {
	OtterSDK->ReportCodecState(true);
    TimePosition = 0;
    TimeSeeking  = 0;
	int ret = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
	OtterSDK->ReportCodecState(false);
	return ret;
}

mad_flow MusicCodec::InputRequest(mad_stream* stream) {
	int Offset = long(stream->this_frame) - long(str);

	if ((Offset > 0) && (Offset < FileBufferSize)) {
		int Offset2 = FileBufferSize - Offset;
		std::memcpy(str, str + Offset, Offset2);
		file->read(str + Offset2, Offset);
	} else
		file->read(str, FileBufferSize);
	mad_stream_buffer(stream, (const unsigned char*)str, FileBufferSize);

	if (file->good())
		return MAD_FLOW_CONTINUE;
	else
		return MAD_FLOW_STOP;
}

mad_flow MusicCodec::OutputPreCallback(const mad_header *header, mad_pcm *pcm) {
	int Channels = pcm->channels;
	int Samples = pcm->length;
	int Frequency = pcm->samplerate;

	if (OtterSDK->GetSamplingFrequency() != pcm->samplerate)
		OtterSDK->ChangeTrackInfo(InfoFrequency, pcm->samplerate);
	if (OtterSDK->GetChannels() != Channels)
		OtterSDK->ChangeTrackInfo(InfoChannels, Channels);

	signed int DataO;
	//if (!OtterSDK.CodecOutputCallback(pcm->samples, Samples, Channels))
		//return MAD_FLOW_BREAK;
    int TimePosition2 = TimePosition;
    TimePosition += Samples*1000/Frequency;
    if (TimeSeeking) {
        if ((TimeSeeking > TimePosition2/10) && (TimeSeeking < TimePosition/10)) {
            BufferPosition = 0;
            TimeSeeking = 0;
            OtterSDK->DropPCMDevice();
        } else {
            if (TimeSeeking > TimePosition/10) {
                return MAD_FLOW_CONTINUE;
            }
            if (TimeSeeking < TimePosition/10) {
                file->seekg(0);
                TimePosition = 0;
                return MAD_FLOW_CONTINUE;
            }
        }
    }

	for (int i = 0; i < Samples; i++) {
		if (BufferPosition > OtterSDK->GetPCMBufferSize() - 4) {
			BufferPosition = 0;
			if (!OtterSDK->CodecPCMSignalAndWait())
				return MAD_FLOW_BREAK;
		}

		DataO = Scale(pcm->samples[0][i]);
		OtterSDK->GetPCMBuffer()[BufferPosition++] = (DataO >> 0 ) & 0xff;
		OtterSDK->GetPCMBuffer()[BufferPosition++] = (DataO >> 8 ) & 0xff;
		DataO = Scale(pcm->samples[1][i]);
		OtterSDK->GetPCMBuffer()[BufferPosition++] = (DataO >> 0 ) & 0xff;
		OtterSDK->GetPCMBuffer()[BufferPosition++] = (DataO >> 8 ) & 0xff;
	}
	return MAD_FLOW_CONTINUE;
}
